package socketmvc.server.context;

import org.tio.core.ChannelContext;
import org.tio.core.Node;
import org.tio.core.Tio;
import org.tio.core.TioConfig;
import org.tio.utils.lock.SetWithLock;
import socketmvc.core.config.IpConfigType;
import socketmvc.core.config.SocketMvcConfig;
import socketmvc.core.packets.IOPacket;
import socketmvc.core.packets.ResponseFuture;
import socketmvc.core.util.StringUtils;
import socketmvc.core.packets.basic.IPacket;

public class SocketChannel implements IChannelContext {

    private ChannelContext target;

    private String type;

    private IPacket packet;

    private IServerContext serverContext;

    protected SocketChannel(){};

    public SocketChannel(ChannelContext target, String type) {
        this.target = target;
        this.type = type;
    }

    /**
     * 获取im服务上下文
     * @return context
     */
    public IServerContext getServerContext() {
        return serverContext;
    }

    public void setServerContext(IServerContext serverContext) {
        this.serverContext = serverContext;
    }

    /**
     * 当前数据包
     * 当接受到客户端的数据包之后，该函数返回的就是当前接受到的数据包
     * @return packet
     */
    @Override
    public IPacket getPacket() {
        return packet;
    }

    public void setPacket(IPacket packet) {
        this.packet = packet;
    }

    /**
     * 获取附加值
     * @param key k
     * @return v
     */
    @Override
    public Object getAttr(String key){
       return target.get(key);
    }


    /**
     * 设置附加值
     * @param key k
     * @param val v
     * @return v
     */
    @Override
    public Object setAttr(String key, Object val){
         target.set(key,val);
         return val;
    }

    @Override
    public Object removeAttr(String key) {
        Object attr = getAttr(key);
        target.remove(key);
        return attr;
    }

    @Override
    public String getType() {
        return type;
    }

    public TioConfig getIoConfig() {
        return target.getTioConfig();
    }

    /**
     * 发送数据包到当前context
     * @param packet 数据包
     */
    @Override
    public void send(IPacket packet){
        Tio.send(target,wrapPacket(packet));
    }

    /**
     * 发送数据包到当前context,并等待响应结果
     *
     * @param packet 数据包
     * @return 等待结果
     */
    @Override
    public ResponseFuture sendSync(IPacket packet) {
        ResponseFuture responseFuture = (ResponseFuture) IChannelContext.super.createResponseFuture(packet);
        this.send(packet);
        return responseFuture;
    }

    /**
     * 将数据包发送到绑定user的context
     * @param user user
     * @param packet 数据包
     * @return 发送是否成功
     */
    @Override
    public boolean sendToUser(String user, IPacket packet){
       return Tio.sendToUser(getIoConfig(),user,wrapPacket(packet));
    }

    /**
     * 将数据包发送到绑定user的context,并等待响应结果
     *
     * @param user   user
     * @param packet 数据包
     * @return 等待结果
     */
    @Override
    public ResponseFuture sendToUserSync(String user, IPacket packet) {
        ResponseFuture responseFuture = (ResponseFuture) IChannelContext.super.createResponseFuture(packet);
        this.sendToUser(user,packet);
        return responseFuture;
    }

    /**
     * 将数据包发送到绑定group的context
     * @param group group
     * @param packet 数据包
     * @return 发送是否成功
     */
    @Override
    public void sendToGroup(String group, IPacket packet){
        Tio.sendToGroup(getIoConfig(),group,wrapPacket(packet));
    }

    /**
     * 将当前context绑定到user
     * @param user user
     */
    @Override
    public void bindUser(String user){
        Tio.bindUser(target,user);
    }

    /**
     * 将当前context绑定到group
     * @param group group
     */
    @Override
    public void bindGroup(String group){
        Tio.bindGroup(target,group);
    }

    /**
     * 当前context与user解绑
     * @param user user
     */
    @Override
    public void unbindUser(String user){
        Tio.unbindUser(getIoConfig(),user);
    }

    /**
     * 当前context与group解绑
     * @param group group
     */
    @Override
    public void unbindGroup(String group){
        Tio.unbindGroup(group,target);
    }

    @Override
    public boolean getUserIsOnline(String user) {
        SetWithLock<ChannelContext> us = Tio.getByUserid(getIoConfig(), user);
        return us != null && us.size() > 0;
    }

    /**
     * 关闭当前context
     * 注意：关闭意味着彻底抛弃链接，关闭后就不能再发送消息了
     */
    @Override
    public void close(){
        Tio.close(target,"server actively disconnected",ChannelContext.CloseCode.USER_CODE_10);
    }

    /**
     * 将客户端设置进黑名单
     */
    @Override
    public void setContextToBlacklist() {
        Tio.IpBlacklist.add(getIoConfig(),getClientIp());
    }

    /**
     * 从黑名单中删除客户端
     */
    @Override
    public void delContextFromBlacklist() {
        Tio.IpBlacklist.remove(getIoConfig(),getClientIp());
    }

    /**
     * 查看客户端是否在黑名单中
     *
     * @return true在黑名单中，false不在
     */
    @Override
    public boolean isContextInBlacklist() {
        return Tio.IpBlacklist.isInBlacklist(getIoConfig(),getClientIp());
    }

    /**
     * 获取客户端host信息
     *
     * @return 客户端host信息
     */
    @Override
    public SocketMvcConfig.IpConfig getClientConnInfo() {
        Node clientNode = target.getClientNode();
        return new SocketMvcConfig.IpConfig(){
            @Override
            public boolean isEnable() {
                return false;
            }
            @Override
            public String getId() {
                return clientNode.toString();
            }
            @Override
            public IpConfigType getType() {
                return getServerConnInfo().getType();
            }
            @Override
            public String getHost() {
                return clientNode.getIp();
            }
            @Override
            public int getPort() {
                return clientNode.getPort();
            }
            @Override
            public SocketMvcConfig.SSL getSsl() {
                return getServerConnInfo().getSsl();
            }
        };
    }

    /**
     * 获取服务端host信息
     *
     * @return 服务端host信息
     */
    @Override
    public SocketMvcConfig.IpConfig getServerConnInfo() {
        return serverContext.getMvcConfig().getIpConfig();
    }

    public String getClientIp() {
        return target.getClientNode().getIp();
    }


    public int getClientPort() {
        return target.getClientNode().getPort();
    }

    public ChannelContext getTarget() {
        return target;
    }

    private IOPacket wrapPacket(IPacket packet){
        if (packet == null){
            return null;
        }
        // 当前要发送的数据包中未标识cmd命令码的情况下
        if (StringUtils.isEmpty(packet.getCommand())){
            // 优先从本context对象中取持有的数据包，将要发送的包的cmd与context数据包设置为一致
            boolean success = setPacketCmd(this.getPacket(), packet);
            if (!success){
                // 当从本context设置cmd失败后, 从线程对象中获取当前线程持有的context,并再次尝试设置cmd
                SocketChannel threadContext = ThreadSocketChannelHandler.get();
                if (threadContext != null){
                    setPacketCmd(threadContext.getPacket(),packet);
                }
            }
        }
        return new IOPacket(packet);
    }

    private boolean setPacketCmd(IPacket origin,IPacket target){
        if (origin != null){
            target.setCommand(origin.getCommand());
            return true;
        }
        return false;
    }
}
